package java8timeapi;

import org.junit.Test;

import java.util.Date;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.*;
import java.util.Locale;

/**
 * @author lilingzhi
 * @since 2023/1/5 14:49
 */
public class LocalDateTimeDemo {

    @Test
    public void test1(){
        //通过TemporalField 读取localDate相应得知
        LocalDate now = LocalDate.now();
        System.out.println(now.get(ChronoField.DAY_OF_WEEK));
    }

    /**
     * 。从计算机的角度来看，建模时间最自然的格式是表
     * 示一个持续时间段上某个点的单一大整型数
     */
    @Test
    public void test2(){
        Instant now = Instant.now();
        System.out.println(now);
        //静态工厂方法ofEpochSecond传递一个代表秒数的值创建一个该类的实例
        System.out.println(Instant.ofEpochSecond(3L));
        // 静态工厂方法ofEpochSecond 第二个参数通过调整纳秒参数
        System.out.println(Instant.ofEpochSecond(3L, 1_000_000_000L));
        // 由于Instant 是便于机器理解,因此下面则会异常处理 UnsupportedTemporalTypeException
//        int i = now.get(ChronoField.DAY_OF_MONTH);
    }

    /**
     * 定义Duration或Period
     */
    @Test
    public void test3(){
        //比较时间参数
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime end = now.plusDays(1L);
        //86400
        Duration between = Duration.between(now, end);
        System.out.println(between.toMinutes());
        Instant instant = Instant.now();
        // 由于Instant 和LocalDate 处于不同的涉及目的,因此不能直接通过Duration与之比较
        //System.out.println(Duration.between(instant, now).getSeconds());
        Duration from = Duration.from(Duration.ofHours(1L));
        // Duration 只能针对秒和纳秒进行解析
        System.out.println(from.get(ChronoUnit.NANOS));

        System.out.println(Duration.parse("PT20.345S"));
    }

    @Test
    public void test4(){
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime months = now.plusMonths(1L);
        System.out.println(LocalDate.from(now));
        System.out.println(now.format(DateTimeFormatter.ofPattern("yyyy")));
        // 创建一个新的日期,它的值为同一个月一周第几天
        System.out.println(months.with(TemporalAdjusters.dayOfWeekInMonth(1, DayOfWeek.FRIDAY)));

        // 创建一个新日期,firstDayOfMonth 当月第一天
        System.out.println(now.with(TemporalAdjusters.firstDayOfMonth()));

        // firstDayOfNextMonth 下个月第一天

        // firstDayOfNextYear 明年第一天

        // firstDayOfYear 当年第一天

        // firstInMonth 同一个月,第一个符合星期要求的值
        System.out.println(now.with(TemporalAdjusters.firstInMonth(DayOfWeek.THURSDAY)));

        // lastDayOfMonth  当月的最后一天
        System.out.println(now.with(TemporalAdjusters.lastDayOfMonth()));

        // lastDayOfNextMonth 下个月的最后一天

        // lastDayOfNextYear 明年最后一天

        // lastDayOfYear 今年最后一天

        // lastInMonth 同一个月,最后符合星期几的要要求的值

        // 指定下一个星期符合的日期
        System.out.println(now.with(TemporalAdjusters.next(DayOfWeek.THURSDAY)));

        // 指定下一个星期是否符,如果符合则返回当前对象
        System.out.println(now.with(TemporalAdjusters.nextOrSame(DayOfWeek.WEDNESDAY)));

        System.out.println(now.with(TemporalAdjusters.lastDayOfMonth()));

    }

    @Test
    public void test5(){
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime with = now.with(new NextWorkingDay());
        System.out.println(with);
    }

    @Test
    public void test6(){
        LocalDateTime now = LocalDateTime.now();
        //使用lambda
        TemporalAdjuster temporalAdjuster = TemporalAdjusters.ofDateAdjuster(
                temporal -> {
                    DayOfWeek dow = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
                    int dayToAdd = 1;
                    if (dow == DayOfWeek.FRIDAY) dayToAdd = 3;
                    else if (dow == DayOfWeek.SATURDAY) dayToAdd = 2;
                    return temporal.plus(dayToAdd, ChronoUnit.DAYS);
                });
        System.out.println(now.with(temporalAdjuster));
    }


    /**
     * 解析与打印
     */
    @Test
    public void test(){
        LocalDate parse = LocalDate.parse("2022-01-01", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        System.out.println(parse);
        String yyyyMMdd = parse.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        System.out.println(yyyyMMdd);
        DateTimeFormatter dateTimeFormatter = new DateTimeFormatterBuilder()
                .appendText(ChronoField.DAY_OF_WEEK)
                .appendLiteral(". ")
                .appendText(ChronoField.MONTH_OF_YEAR)
                .appendLiteral(". ")
                .appendText(ChronoField.YEAR)
                .parseCaseInsensitive()
                .toFormatter(Locale.CHINA);
        System.out.println(parse.format(dateTimeFormatter));

    }


    // Date || LocalDate || LocalDateTime || Instant 之间相互转换
    @Test
    public void test7(){
        // LocalDate || LocalDateTime ------> instant
        Instant instant = LocalDateTime.now().toInstant(ZoneOffset.UTC);
        System.out.println(instant);

        LocalDate now1 = LocalDate.now();
        System.out.println(now1);
        System.out.println(now1.atStartOfDay(ZoneOffset.UTC).toInstant());

        // instant -----> LocalDateTime || LocalDate
        Instant now = Instant.now();
        System.out.println(LocalDateTime.ofInstant(now, ZoneId.systemDefault()));

        System.out.println(now.atZone(ZoneId.systemDefault()).toLocalDate());

        // LocalDateTime ----> Date
        LocalDateTime localDateTime = LocalDateTime.now();
        Date from = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());


        // LocalDate -----> Date
        Date from1 = Date.from(LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant());
        System.out.println(from1);

        // Date ------> LocalDateTime
        Date date = new Date();
        LocalDateTime dateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();

        // Date  -----> LocalDate
        LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    }
}


// 制作nextWorkingDay

class NextWorkingDay implements TemporalAdjuster{

    /**
     * Adjusts the specified temporal object.
     * <p>
     * This adjusts the specified temporal object using the logic
     * encapsulated in the implementing class.
     * Examples might be an adjuster that sets the date avoiding weekends, or one that
     * sets the date to the last day of the month.
     * <p>
     * There are two equivalent ways of using this method.
     * The first is to invoke this method directly.
     * The second is to use {@link Temporal#with(TemporalAdjuster)}:
     * <pre>
     *   // these two lines are equivalent, but the second approach is recommended
     *   temporal = thisAdjuster.adjustInto(temporal);
     *   temporal = temporal.with(thisAdjuster);
     * </pre>
     * It is recommended to use the second approach, {@code with(TemporalAdjuster)},
     * as it is a lot clearer to read in code.
     *
     * @param temporal the temporal object to adjust, not null
     * @return an object of the same observable type with the adjustment made, not null
     * @throws DateTimeException   if unable to make the adjustment
     * @throws ArithmeticException if numeric overflow occurs
     * @implSpec The implementation must take the input object and adjust it.
     * The implementation defines the logic of the adjustment and is responsible for
     * documenting that logic. It may use any method on {@code Temporal} to
     * query the temporal object and perform the adjustment.
     * The returned object must have the same observable type as the input object
     * <p>
     * The input object must not be altered.
     * Instead, an adjusted copy of the original must be returned.
     * This provides equivalent, safe behavior for immutable and mutable temporal objects.
     * <p>
     * The input temporal object may be in a calendar system other than ISO.
     * Implementations may choose to document compatibility with other calendar systems,
     * or reject non-ISO temporal objects by {@link TemporalQueries#chronology() querying the chronology}.
     * <p>
     * This method may be called from multiple threads in parallel.
     * It must be thread-safe when invoked.
     */
    @Override
    public Temporal adjustInto(Temporal temporal) {

        DayOfWeek dow = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
        int dayToAdd = 1;
        if (dow == DayOfWeek.FRIDAY)  dayToAdd = 3;
        else if(dow == DayOfWeek.SATURDAY) dayToAdd = 2;
        return temporal.plus(dayToAdd,ChronoUnit.DAYS);
    }
}


